
package com.hlkj.pay.app.merchant.ext.impl.gtzfpay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.gtzfpay.constant.GtzfPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.gtzfpay.dto.GtzfPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.gtzfpay.dto.GtzfpayAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.gtzfpay.utils.GtzfPayRsaUtil;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.PayOrderFundDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.MerchantUtils;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author HlpayTeam
 * @date 2024/10/15 18:16
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_GTZF_PAY)
public class GtzfPayMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    @Value("${hlkj.pay.openapi.checkSign:true}")
    private boolean checkSign;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
//        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
//        String easyPay = getPayType(payWayDO);
//        if (!StringUtils.hasText(easyPay)) {
//            channelPayOrderContext.setRemark("当前支付方式不支持");
//            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
//            return null;
//        }
//        return extensionExecutor.execute(EasyScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.channelOrderCode(channelPayOrderContext));
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        PayOrderDO payOrderDO = channelPayOrderContext.getPayOrderDO();
        if (StringUtils.hasText(payOrderDO.getAcctInfoJson())) {
            List<PayOrderFundDto> accounts = JsonUtils.parseArray(payOrderDO.getAcctInfoJson(), PayOrderFundDto.class);
            for (PayOrderFundDto orderAccount : accounts) {
                if (!StringUtils.hasText(orderAccount.getSepaFeeRatio())) {
                    channelPayOrderContext.setRemark("国通分账规则,分账手续费承担比例必填");
                    channelPayOrderContext.setProcessStatus(Boolean.FALSE);
                    return;
                }
                if (orderAccount.getPeriodType() == null) {
                    orderAccount.setPeriodType(CommonEnum.YES_NO_TYPE.NO.getCode());
                }
            }
            double sum = accounts.stream().mapToDouble(orderFundDetailDO -> new Double(orderFundDetailDO.getAmount())).sum();
            Double sepaFeeRatio = accounts.stream().mapToDouble(orderAccount -> new Double(orderAccount.getSepaFeeRatio())).sum();
            if (sepaFeeRatio == null) {
                sepaFeeRatio = new Double(0);
            }
            if (payOrderDO.getAmount().compareTo(new BigDecimal(sum)) > 0) {
                MerchantAppChannelDO merchantAppChannelDO = channelPayOrderContext.getMerchantAppChannelDO();
                GtzfpayAppChannelParams easypayAppChannelParams = JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), GtzfpayAppChannelParams.class);
                BigDecimal subtract = payOrderDO.getAmount().subtract(new BigDecimal(sum));
                PayOrderFundDto oneAccount = accounts.get(0);
                PayOrderFundDto orderAccount = new PayOrderFundDto();
                orderAccount.setAmount(MerchantUtils.bigDecimalToString2(subtract));
                orderAccount.setAccountNo(easypayAppChannelParams.getCustId());
                orderAccount.setDescription("多余部分分给自己");
                orderAccount.setPeriodType(oneAccount.getPeriodType());
                orderAccount.setSepaFeeRatio(100 - sepaFeeRatio.intValue() + "");
                orderAccount.setBatchNo(oneAccount.getBatchNo());
                orderAccount.setBatchNoDetail(oneAccount.getBatchNo() + "_" + (accounts.size() + 1));
                accounts.add(orderAccount);
                payOrderDO.setAcctInfoJson(JsonUtils.toJsonString(accounts));
            }
            channelPayOrderContext.setAccounts(accounts);
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));

    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return false;
        }
        return extensionExecutor.execute(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.refundApply(channelRefundPayOrderContext));
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));

    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String easyPay = getPayType(payWayDO);
        if (!StringUtils.hasText(easyPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(GtzfScenePayOrderExtPt.class, BizScenario.valueOf(easyPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest servletRequest, ProviderChannelConfigDO providerChannelConfigDO) {
        String requestContext = null;
        try {
            // 获取完整的 JSON 字符串
            requestContext = params(servletRequest);
        }
        catch (Exception e) {
            log.error("从当前线程中获取参数失败 从流中重新获取失败:{}", e.getMessage(),e);
            return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
        }
        if (!StringUtils.hasText(requestContext)) {
            log.info("国通回调失败，未获取到回调参数:{}", requestContext);
            return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
        }
        JSONObject obj = JSON.parseObject(requestContext);
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        GtzfPayConfig config = JsonUtils.parseObject(channelConfig, GtzfPayConfig.class);
        log.info("国通回调参数:{}", requestContext);
        boolean verifySign = GtzfPayRsaUtil.verify(requestContext, config.getMchPublicKey());
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = processCommonCall(obj);
        payOrderDOCommonResult.getData().setCallBackBody(requestContext);
        if (!verifySign && checkSign) {
            log.error("国通签名错误:{}", "验签失败");
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            return payOrderDOCommonResult;
        }
        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest callBackBody, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return getPayType(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        CommonEnum.COMMON_APP_TYPE commonAppType = null;
        switch (PayEnums.PAY_TYPE.fromByCode(payWayDO.getType())) {
            case WECHAT:
                switch (scenePayType) {
                    case QR:
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_PUB;
                        break;
                    // case MINI_APP:
                    case H5:
                    case APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_MIN;
                        break;
                }
                break;
            case ALIPAY:
                switch (scenePayType) {
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_PUB;
                        break;
                    case MINI_APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_MIN;
                        break;
                }
                break;
        }
        if (commonAppType == null) {
            return null;
        }

        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        switch (commonAppType) {
            case WECHAT_PUB:
                return providerChannelConfigDO.getWechatDefaultPubId();
            case WECHAT_MIN:
                return providerChannelConfigDO.getWechatDefaultMiniId();
            case ALIPAY_PUB:
                return providerChannelConfigDO.getAlipayPubId();
            case ALIPAY_MIN:
                return providerChannelConfigDO.getAlipayMiniId();
        }
        return null;
    }

    @Override
    public String callBackResponse(CommonResult commonResult) {
        // 返回内容需要传机构号，暂取不到，需要修改整体逻辑
        ProviderChannelConfigDO providerChannelConfigDO = (ProviderChannelConfigDO) commonResult.getData();
        Map<String, String> retObj = new HashMap<>();
        if (commonResult.isSuccess()) {
            retObj.put("rspCod", "0000");
            retObj.put("retMsg", "success");
        }
        else {
            retObj.put("retCod", "9999");
            retObj.put("retMsg", "error");
        }
        return retObj.toString();
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        // 额外参数信息
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if(extra.getOriginalType() == CommonEnum.YES_NO_TYPE.NO.getCode()){
            PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.from(channelPayOrderContext.getPayWayDO().getSubType());
            if (scenePayType == null) {
                return false;
            }
            PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(channelPayOrderContext.getPayWayDO().getType());
            if(payType == null){
                return false;
            }
            switch (scenePayType) {
                case H5:
                case APP:
                    if(payType == PayEnums.PAY_TYPE.WECHAT){
                        return true;
                    }
            }
        }
        return false;
    }

    /**
     * 汇付支付方式路由
     *
     * @param payWayDO
     * @return
     */
    private String getPayType(PayWayDO payWayDO) {
        log.info("国通实际支付type:{}", payWayDO.getSubType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        if (scenePayType == null) {
            return null;
        }
        switch (payType) {
            case WECHAT:
                switch (scenePayType) {
                    case SCAN:
                        return GtzfPayExtConstant.SCAN_PAY;
                    case H5:
                    case APP:
                    case MINI_APP:
                        return GtzfPayExtConstant.H5;
                }
                return GtzfPayExtConstant.JSAPI_PAY;
            case ALIPAY:
                switch (scenePayType) {
                    case QR:
                    case H5:
                    case APP:
                    case JSAPI:
                        return GtzfPayExtConstant.NATIVE_PAY;
                    case MINI_APP:
                        return GtzfPayExtConstant.JSAPI_PAY;
                    case SCAN:
                        return GtzfPayExtConstant.SCAN_PAY;
                    default:
                        return null;
                }
            case UNION_APY:
                switch (scenePayType) {
                    case SCAN:
                        return GtzfPayExtConstant.SCAN_PAY;
                }
                return GtzfPayExtConstant.NATIVE_PAY;

        }
        return null;
    }

    /**
     * 通用回调参数解析
     *
     * @param respOrderInfo
     * @return
     */
    CommonResult<PayOrderChannelResultDto> processCommonCall(JSONObject respOrderInfo) {
        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        CommonResult<PayOrderChannelResultDto> commonResult = CommonResult.success(payOrderChannelResultDto);
        if ("1".equals(respOrderInfo.getString("ORDER_STATUS"))) {
            updartePayOrderDO.setPayOrderNo(respOrderInfo.getString("THREE_ORDER_NO"));
            updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
            if (StringUtils.hasText(respOrderInfo.getString("ORDER_TIME"))) {
                try {
                    updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(respOrderInfo.getString("ORDER_TIME")));
                }
                catch (Exception e) {
                    log.error("国通订单查询返回 时间格式化错误 dateTime:{},message:{}", respOrderInfo.getString("ORDER_TIME"), e.getMessage(),e);
                }
            }

            if (StringUtils.hasText(respOrderInfo.getString("NETR_AMT"))) {
                updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(respOrderInfo.getInteger("NETR_AMT")));
            }
            updartePayOrderDO.setInsOrderNo(respOrderInfo.getString("ORDER_NO"));
            updartePayOrderDO.setChannelOrderNo(respOrderInfo.getString("T_PAY_NO"));
            updartePayOrderDO.setUserId(respOrderInfo.getString("SUB_OPEN_ID"));
        }
        else {
            commonResult.setMsg(respOrderInfo.getString("REMARK"));
        }
        return commonResult;
    }

    String params(HttpServletRequest request) throws Exception {
        // 读取请求体中的内容
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        log.warn("国通回调返回参数:{}", stringBuilder.toString());
        // 获取完整的 JSON 字符串
        return stringBuilder.toString();
    }
}
